#include "LOGR_xml_parse_conf.h"

#include "xmlparse.h"

#define NO_ERROR 0
#define SYNTAX_ERROR 1

enum xml_conf_tags {XML_CONF_NULL, XML_CONF_CONF, XML_CONF_WS, XML_CONF_IF, 
		     XML_CONF_OF, XML_CONF_SN, XML_CONF_LF, XML_CONF_GF,
                     XML_CONF_H, XML_CONF_US, XML_CONF_LMLF, XML_CONF_HR};

typedef enum xml_conf_tags xtags;

#define MAX_STACK 10

typedef struct xml_Data {
  XML_Parser parser;
  int itag;
  xtags stack[MAX_STACK];
  xtags wtag;
  ConfigNodePtr cnp;
  OutputFileNodePtr cof;
  int line_no;
  int error;
} XDATA;
  
typedef XDATA *PXDATA;

static xtags
find_tag(const char *name)
{
  if(!strcasecmp(name,"Configuration"))
    return XML_CONF_CONF;
  else if(!strcasecmp(name,"WebServer"))
    return XML_CONF_WS;
  else if(!strcasecmp(name,"Subnet"))
    return XML_CONF_SN;
  else if(!strcasecmp(name,"InputFiles"))
    return XML_CONF_IF;
  else if(!strcasecmp(name,"LogFile"))
    return XML_CONF_LF;
  else if(!strcasecmp(name,"Host"))
    return XML_CONF_H;
  else if(!strcasecmp(name,"GraphFile"))
    return XML_CONF_GF;
  else if(!strcasecmp(name,"OutputFiles"))
    return XML_CONF_OF;
  else if(!strcasecmp(name,"UserSession"))
    return XML_CONF_US;
  else if(!strcasecmp(name,"LogMLFile"))
    return XML_CONF_LMLF;
  else if(!strcasecmp(name,"HTMLReport"))
    return XML_CONF_HR;
  return XML_CONF_NULL;
}

void
set_webserver_info(void *userData, const char *name, const char **atts)
{
    PXDATA px = (PXDATA)userData;
    ConfigNodePtr cnp = px ? px->cnp : NULL;
    int i=0;
    char *val = NULL;

    if(!px || !atts)
	return;

    while(atts[i])
    {
	if(!strcmp(atts[i], "uri"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
		LOGR_set_config_webserver_uri(cnp,val);
	} 
	else if(!strcmp(atts[i], "domain"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
		LOGR_set_config_webserver_domain(cnp,val);
	}
	i++;
    }
}

void
set_subnet_info(void *userData, const char *name, const char **atts)
{
    PXDATA px = (PXDATA)userData;
    ConfigNodePtr cnp = px ? px->cnp : NULL;
    int i=0;
    char *val = NULL;

    if(!px || !atts)
	return;

    while(atts[i])
    {
	if(!strcmp(atts[i], "cidr"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
		LOGR_add_config_subnet(cnp,val);
	} 
	i++;
    }    
}

void
set_logfile_info(void *userData, const char *name, const char **atts)
{
    PXDATA px = (PXDATA)userData;
    ConfigNodePtr cnp = px ? px->cnp : NULL;
    int i=0;
    char *val = NULL;

    if(!px || !atts)
	return;

    while(atts[i])
    {
	if(!strcmp(atts[i], "name"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
	    {
		LOGR_set_config_logfile_name(cnp,val);
	    }
	}
	else if(!strcmp(atts[i], "uri_prefix"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
	    {
		LOGR_set_config_logfile_uri_prefix(cnp,val);
	    }
	}
	else if(!strcmp(atts[i], "prefix"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
	    {
		LOGR_set_config_logfile_prefix(cnp,val);
	    }
	}
	else if(!strcmp(atts[i], "root_directory"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
	    {
		LOGR_set_config_logfile_root_dir(cnp,val);
	    }
	}
	else if(!strcmp(atts[i], "type"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
	    {
		if(!strcmp(val,"extended"))
		{
		    LOGR_set_config_logfile_type(cnp,LOGR_EXTENDED_LOG);
		}
		else if(!strcmp(val,"common"))
		{
		    LOGR_set_config_logfile_type(cnp,LOGR_COMMON_LOG);
		}
	    }
	}
	else if(!strcmp(atts[i], "compute_user_sessions"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && !strcmp(val,"no"))
		LOGR_reset_config_compute_user_sessions(cnp);
	    else if(val && !strcmp(val,"yes"))
		LOGR_set_config_compute_user_sessions(cnp);
	}
	i++;
    }    
}

void
set_host_info(void *userData, const char *name, const char **atts)
{
    PXDATA px = (PXDATA)userData;
    ConfigNodePtr cnp = px ? px->cnp : NULL;
    int i=0;
    char *val = NULL;

    if(!px || !atts)
	return;

    while(atts[i])
    {
	if(!strcmp(atts[i], "resolve_ip"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
	    {
		if(!strcmp(val,"yes"))
		{
		    LOGR_set_config_logfile_res_ip(cnp,1);
		}
		else if(!strcmp(val,"no"))
		{
		    LOGR_set_config_logfile_res_ip(cnp,0);
		}
	    }
	}
	else if(!strcmp(atts[i], "resolve_ip_timeout"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
	    {
		int tout = atoi(val);
		LOGR_set_config_logfile_res_ip_timeout(cnp,tout);
	    }
	}
	i++;
    }
}

void
set_outputfile_info(void *userData, const char *name, const char **atts)
{
    PXDATA px = (PXDATA)userData;
    ConfigNodePtr cnp = px ? px->cnp : NULL;
    int i=0;
    int nele = -1;
    int nkey = -1;
    char *val = NULL;

    if(!px || !atts)
	return;

    while(atts[i])
    {
	if(!strcmp(atts[i], "nelements"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
		nele = atoi(val);
	}

	else if(!strcmp(atts[i], "nkeywords"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
		nkey = atoi(val);
	}
	i++;
    }
    px->cof = LOGR_add_output_file(cnp,nele,nkey);
}

void
set_user_session_info(void *userData, const char *name, const char **atts)
{
    PXDATA px = (PXDATA)userData;
    OutputFileNodePtr cof = px ? px->cof : NULL;
    int i=0;
    int num_users = -1;
    int num_graphs = -1;
    int max_edges = -1;
    int min_edges = -1;
    char *val = NULL;

    if(!px || !atts)
	return;

    while(atts[i])
    {
	if(!strcmp(atts[i], "nusers"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
		num_users = atoi(val);
	}
	else if(!strcmp(atts[i], "ngraphs"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
		num_graphs = atoi(val);
	}
	else if(!strcmp(atts[i], "min_edges"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
		min_edges = atoi(val);
	}
	else if(!strcmp(atts[i], "max_edges"))
	{
	    i++;
	    val = (char *)atts[i];
	    if(val && val[0]!='\0')
		max_edges = atoi(val);
	}
	i++;
    }
    LOGR_set_user_session(cof,num_users, num_graphs, min_edges, max_edges);
}

void
set_logmlfile_info(void *userData, const char *name, const char **atts)
{
    PXDATA px = (PXDATA)userData;
    OutputFileNodePtr cof = px ? px->cof : NULL;
    int i=0;
    char *fname = NULL;
    char *dir = NULL;
    char *lmlname = NULL;

    if(!px || !atts || !cof)
	return;

    while(atts[i])
    {
	if(!strcmp(atts[i], "name"))
	{
	    i++;
	    fname = (char *)atts[i];
	}
	else if(!strcmp(atts[i], "dir"))
	{
	    i++;
	    dir = (char *)atts[i];
	}
	i++;
    }

    if(fname && fname[0]!='\0') 
    {
	if(dir && dir[0]!='\0')
	{
	    GRStrAllocCopy(lmlname,dir);
	    GRStrAllocCat(lmlname,"/");
	}
	GRStrAllocCat(lmlname,fname);
    }
    if(lmlname && lmlname[0]!='\0')
	LOGR_set_logmlfile_name(cof,lmlname);

    if(lmlname) {
	free(lmlname); lmlname = NULL;
    }
}

void
set_report_prefix_info(void *userData, const char *name, const char **atts)
{
    PXDATA px = (PXDATA)userData;
    OutputFileNodePtr cof = px ? px->cof : NULL;
    int i=0;

    if(!px || !atts)
	return;

    while(atts[i])
    {
	if(!strcmp(atts[i], "prefix"))
	{
	    i++;
	    LOGR_set_report_prefix_name(cof,(char *)atts[i]);
	}
	else if(!strcmp(atts[i], "dir"))
	{
	    i++;
	    LOGR_set_report_directory_name(cof,(char *)atts[i]);
	}
	i++;
    }
}

void
set_graphfile_info(void *userData, const char *name, const char **atts)
{
    PXDATA px = (PXDATA)userData;
    ConfigNodePtr cnp = px ? px->cnp : NULL;
    int i=0;
    char *fname = NULL;
    char *dir = NULL;
    char *gfname = NULL;

    if(!px || !atts)
	return;

    while(atts[i])
    {
	if(!strcmp(atts[i], "name"))
	{
	    i++;
	    fname = (char *)atts[i];
	}
	else if(!strcmp(atts[i], "dir"))
	{
	    i++;
	    dir = (char *)atts[i];
	}
	i++;
    }
    if(fname && fname[0]!='\0')
    {
	if(dir && dir[0]!='\0')
	{
	    GRStrAllocCopy(gfname,dir);
	    GRStrAllocCat(gfname,"/");
	}
	GRStrAllocCat(gfname,fname);
    }
    if(gfname && gfname[0]!='\0')
	LOGR_set_config_graphfile_name(cnp,gfname);

    if(gfname) {
	free(gfname); gfname = NULL;
    }
}

void
set_configuration_info(void *userData, const char *name, const char **atts)
{
    PXDATA px = (PXDATA)userData;
    xtags ctag = find_tag(name);
    px->itag++; /* PUSH */
    px->stack[px->itag] = ctag;
    px->line_no = XML_GetCurrentLineNumber(px->parser);
    switch(ctag)
    {
    case XML_CONF_CONF:
	if(px->wtag == XML_CONF_NULL)
	    px->wtag = ctag;
	else
	    px->error = SYNTAX_ERROR;
	break;
    case XML_CONF_WS:
	if(px->wtag == XML_CONF_CONF)
	    set_webserver_info(userData,name,atts);
	else
	    px->error = SYNTAX_ERROR;
	px->wtag = ctag;
	break;
    case XML_CONF_SN:
	if(px->wtag == XML_CONF_WS)
	    set_subnet_info(userData,name,atts);
	else
	    px->error = SYNTAX_ERROR;
	px->wtag = ctag;
	break;
    case XML_CONF_IF:
	if(px->wtag == XML_CONF_CONF)
	    px->wtag = ctag;
	else
	    px->error = SYNTAX_ERROR;
	break;
    case XML_CONF_LF:
	if(px->wtag == XML_CONF_IF)
	    set_logfile_info(userData,name,atts);
	else
	    px->error = SYNTAX_ERROR;
	px->wtag = ctag;
	break;
    case XML_CONF_H:
	if(px->wtag == XML_CONF_LF)
	    set_host_info(userData,name,atts);
	else
	    px->error = SYNTAX_ERROR;
	px->wtag = ctag;
	break;
    case XML_CONF_GF:
	if(px->wtag == XML_CONF_IF)
	    set_graphfile_info(userData,name,atts);
	else
	    px->error = SYNTAX_ERROR;
	px->wtag = ctag;
	break;
    case XML_CONF_OF:
	if(px->wtag == XML_CONF_CONF)
	    set_outputfile_info(userData,name,atts);
	else
	    px->error = SYNTAX_ERROR;
	px->wtag = ctag;
	break;
    case XML_CONF_US:
	if(px->wtag == XML_CONF_OF)
	    set_user_session_info(userData,name,atts);
	else
	    px->error = SYNTAX_ERROR;
	px->wtag = ctag;
	break;
    case XML_CONF_LMLF:
	if(px->wtag == XML_CONF_OF)
	    set_logmlfile_info(userData,name,atts);
	else
	    px->error = SYNTAX_ERROR;
	px->wtag = ctag;
	break;
    case XML_CONF_HR:
	if(px->wtag == XML_CONF_OF)
	    set_report_prefix_info(userData,name,atts);
	else
	    px->error = SYNTAX_ERROR;
	px->wtag = ctag;
	break;
    case XML_CONF_NULL:
	break;
    }
}

static void 
startElement(void *userData, const char *name, const char **atts)
{
  PXDATA px = (PXDATA)userData;
  /*int i;
  puts(name);
  i=0;
  while(atts && atts[i++])
    {
      puts(atts[i-1]);
      }*/
  if(px->error == NO_ERROR)
    set_configuration_info(userData, name, atts);
}

static void 
endElement(void *userData, const char *name)
{
    PXDATA px = (PXDATA)userData;
    px->itag--; /* POP */
    px->wtag = px->stack[px->itag];
}

int
LOGR_parse_xml_conf_file(ConfigNodePtr cnp,char *text, long len)
{
  XML_Parser parser = XML_ParserCreate(NULL);

  PXDATA px = (PXDATA) calloc(1,sizeof(XDATA));
  if(!px) {
      fprintf(stderr,"Out of Memory LOGR_parse_xml_conf_file\n");
      abort();
  }
  px->wtag = XML_CONF_NULL;
  px->itag = 0;
  px->stack[0] = XML_CONF_NULL;
  px->cnp = cnp;	
  px->cof = NULL;
  px->parser = parser;
 
  XML_SetUserData(parser,px);
  XML_SetElementHandler(parser, startElement, endElement);

  if (!XML_Parse(parser, text, (size_t)len, 1)) 
    {
      fprintf(stderr,
	      "%s at line %d\n",
	      XML_ErrorString(XML_GetErrorCode(parser)),
	      XML_GetCurrentLineNumber(parser));
      free(px); px = NULL;
      return 1;
    }

  if(px->error != NO_ERROR)
    {
      fprintf(stderr,"Error in line %d\n",px->line_no);
      return px->error;
    }

  XML_ParserFree(parser);
  free(px); px = NULL;
  return 0;
}




